using System;
using System.Data;
using System.Text;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core
{
	#region Documentation.
	/// <summary>
	/// Removes rows from a table.
	/// </summary>
	/// <example>
	/// <para>
	/// The following example deletes all rows from the Employees table.
	/// <code>
	/// DeleteStatement deleteAllEmployees = new DeleteStatement(new EmployeesMeta());
	/// deleteAllEmployees.Execute();
	/// </code>
	/// </para>
	/// The following example deletes all employees that are not referenced in the EmployeeTerritories table.
	/// <para>
	/// <code>
	/// public void DeleteNonReferencedEmployees()
	/// {
	/// 	// This is the equivalent SQL code that does the same task.
	/// 
	/// 	//	DELETE FROM Employees
	/// 	//	WHERE NOT EXISTS 
	/// 	//		(SELECT * 
	/// 	//		FROM EmployeeTerritories 
	/// 	//		WHERE (EmployeeTerritories.EmployeeID = Employees.EmployeeID))
	/// 
	/// 	EmployeesMeta employees = new EmployeesMeta();
	/// 	EmployeeTerritoriesMeta empTerritories = new EmployeeTerritoriesMeta();
	/// 
	/// 	// Subquery that returns all referenced employees. The subquery is used by the NOT EXIST predicate.
	/// 	SelectStatement referencedEmployees = new SelectStatement(empTerritories);
	/// 	referencedEmployees.Where.Add(PredicateFactory.Compare(empTerritories.EmployeeID, "=", employees.EmployeeID));
	/// 
	/// 	DeleteStatement delete = new DeleteStatement(employees);
	/// 	// NOT EXISTS predicate is created when the parameter negate of the PredicateFactory.Exists is set to true.
	/// 	delete.Where.Add(PredicateFactory.Exists(referencedEmployees, true));
	/// 	delete.Execute();
	/// }
	/// </code>
	/// </para>
	/// </example>
	#endregion
	[Serializable]
	public sealed class DeleteStatement : SqlStatementBase
	{
		#region Members.

		SearchCondition where = new SearchCondition();

		#endregion

		/// <summary>
		/// Creates a new DELETE statement that deletes rows from the given table.
		/// </summary>
		/// <param name="fromTable">The table from which the rows are to be removed.</param>
		public DeleteStatement(IDbTable fromTable) 
            : base(fromTable)
		{
        }

		private IDbTable fromTable
		{
			get {return TargetTable;}
		}

		#region Methods.

		/// <summary>
		/// Renders DELETE statement.
		/// </summary>
		/// <param name="dbms">Target DBMS.</param>
		/// <param name="output">StringBuilder to which SQL is appended.</param>
		/// <param name="parameters">SQL parameter collection to which the object's and its children's
		/// parameters are added. After the rendering is done the collection contains all parameters with unique names.</param>
		public override void Render(DbmsType dbms, StringBuilder output, DbParameterCollection parameters)
		{
            IDeleteRenderer renderer = DbmsComponentFactory.GetComponent<IDeleteRenderer>(dbms);
            renderer.Render(this, dbms, output, parameters);
		}

		/// <summary>
		/// Executes the DELETE command.
		/// </summary>
		/// <returns>The number of rows affected.</returns>
		/// <remarks>Uses class' <see cref="IConnectionProvider"/> object.</remarks>
		public int Execute()
		{
			DbParameterCollection parameters = new DbParameterCollection();
			StringBuilder cmdText = new StringBuilder();
			Render(this.ConnectionProvider.DBMS, cmdText, parameters);

			string command = cmdText.ToString();
			this.LastExecutedCommandInfo = new CommandExecutionStatistics(command);
			int rowsAffected = DbUtil.ExecuteNonQuery(this.ConnectionProvider, command, parameters, CommandType.Text);
            this.LastExecutedCommandInfo.StopTime();
			return rowsAffected;
		}

		/// <summary>
		/// Executes the DELETE command.
		/// </summary>
		/// <param name="conn"><see cref="IConnectionProvider"/> that will be used.</param>
		/// <returns>The number of rows affected.</returns>
		public int Execute(IConnectionProvider conn)
		{
			this.ConnectionProvider = conn;
			return Execute();
		}

		#endregion

		#region Properties.

		/// <summary>
		/// Gets the table from which the rows are to be removed.
		/// </summary>
		public IDbTable FromTable
		{
			get {return this.fromTable;}		
		}

		/// <summary>
		/// Gets or sets the <see cref="SearchCondition"/> which limits the number of rows that are deleted.
		/// </summary>
		public SearchCondition Where
		{
			get {return this.where;}
			set {this.where = value;}
		}

		#endregion
	}
}
